iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 15
3

我們接下來會使用 PHPConf 2016 的簡報「使用 Slim 為 Legacy Code 重構」提到的 proxy pattern 方法來重構。而中間的 Route 會使用 Laravel 。只是在使用 Laravel 之前,我們得先要升級 PHP 。

昨天是發現它沒有內建函式 mysql_* ,後面的版本都建議換用 mysqli_* 了。

既然如此,我們就寫一堆 mysql_* 函式,轉接到 mysqli_* 函式就好了呀!

這也是 Adapter Pattern 的應用方法之一,只是這個狀況下,我們更改原始碼的範圍不會很大。

實驗看看

觀查一下程式碼,原本的主要路由 admin.phpindex.php 背後都會引用 config.php ,我們寫一個 workaround.phpconfig.php 裡載入即可。

先來試試連線函式:

if (!function_exists('mysql_connect')) {
    function mysql_connect($host, $user, $pass)
    {
        return mysqli_connect($host, $user, $pass);
    }
}

config.php 載入:

require_once __DIR__ . '/workaround.php';

接著啟動服務:

docker run --rm -it --link mysql -v `pwd`:/source -w /source -p 8080:8080 php:7.2-alpine php -S 0.0.0.0:8080

發現居然沒有 mysqli ,上網可以查得到它有支援,所以看樣子是 alpine 預設沒安裝,來換個策略:先 sh 進去安裝後,再開伺服器:

docker run --rm -it --link some-mysql:mysql -v `pwd`:/source -w /source -p 8080:8080 php:7.2-alpine sh

# 在 Docker 裡
docker-php-ext-install mysqli
php -S 0.0.0.0:8080

哦哦哦!這次又出現不一樣的訊息了:

Warning: Use of undefined constant MYSQL_ASSOC - assumed 'MYSQL_ASSOC' (this will throw an Error in a future version of PHP) in /source/class/mysql.class.php on line 67

Fatal error: Uncaught Error: Call to undefined function mysql_query() in /source/class/mysql.class.php:68 Stack trace: #0 /source/class/mysql.class.php(41): db->init() #1 /source/class/shop.class.php(56): db->__construct(true) #2 /source/index.php(8): shop->__construct(true) #3 {main} thrown in /source/class/mysql.class.php on line 68

所以看起來連線是可行的,我們只要把所有函式都接上去就行了。

實作轉接函式

經過一連串的 try & error 後,最後 workaround.php 的長相如下:

class Workaround
{
    public static $mysqli;
}

define('MYSQL_ASSOC', MYSQLI_ASSOC);

if (!function_exists('mysql_connect')) {
    function mysql_connect($host, $user, $pass)
    {
        return Workaround::$mysqli = mysqli_connect($host, $user, $pass);
    }
}

if (!function_exists('mysql_close')) {
    function mysql_close($link)
    {
        return mysqli_close($link);
    }
}

if (!function_exists('mysql_query')) {
    function mysql_query($query, $link = null)
    {
        if (null === $link) {
            return mysqli_query(Workaround::$mysqli, $query);
        } else {
            return mysqli_query($link, $query);
        }
    }
}

if (!function_exists('mysql_select_db')) {
    function mysql_select_db($dbname, $link)
    {
        return mysqli_select_db($link, $dbname);
    }
}

if (!function_exists('mysql_fetch_array')) {
    function mysql_fetch_array($result, $type)
    {
        return mysqli_fetch_array($result, $type);
    }
}

if (!function_exists('mysql_num_rows')) {
    function mysql_num_rows($result)
    {
        return mysqli_num_rows($result);
    }
}

if (!function_exists('mysql_real_escape_string')) {
    function mysql_real_escape_string($string, $link)
    {
        if (null === $link) {
            return mysqli_real_escape_string(Workaround::$mysqli, $string);
        } else {
            return mysqli_real_escape_string($link, $string);
        }
    }
}

if (!function_exists('mysql_errno')) {
    function mysql_errno($link = null)
    {
        if (null === $link) {
            return mysqli_errno(Workaround::$mysqli);
        } else {
            return mysqli_errno($link);
        }
    }
}

if (!function_exists('mysql_error')) {
    function mysql_error($link = null)
    {
        if (null === $link) {
            return mysqli_error(Workaround::$mysqli);
        } else {
            return mysqli_error($link);
        }
    }
}

會有 Workaround class 的目的是為了暫存 mysqli 連線變數。且也在 function 先做好手腳了,這樣在之後調整程式碼會比較簡單一點。

原始碼只有修改兩個地方,一個是 config.php 的引用,另一個是 mysql.class.php 有個地方把字串當陣列在用, PHP 7.2 不支援,因此只能修改了。

基本上,上面這樣就算完成了,未來就可以使用 PHP 7.2 開發了。

升級的過程還會有哪些雷?

筆者目前有遇過的: 5.3 to 5.6 下面這個狀況會報錯:

foo(&$bar);

function foo($bar) {
}

要改成下面這樣

foo($bar);

function foo(&$bar) {
}

其他就沒有遇過了, PHP 算是相容性做很好的語言。但跟大多數語言和框架一樣,升級還是無法確定一切正常,只能靠亂點測試的運氣。更好的方法是寫自動化測試,在升級的時候跑一輪即可。

只是一個長久未重構的既有程式碼(legacy code),無法簡單地寫自動化測試,只能先使用硬上的方法讓程式變得比較好測之後,再開始把測試一個一個補上去。

參考資料

程式碼可參考 GitHub PR


上一篇
重構的第一步--讓程式可以動
下一篇
導入 Composer
系列文
看到 code 寫成這樣我也是醉了,不如試試重構?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
wonton
iT邦高手 6 級 ‧ 2022-01-25 09:02:05

我剛從 php5 升級到 php8,有一個舊系統都使用 mysql 開頭的函式,看到這篇真的是幫上大忙了,非常感謝。

Miles iT邦新手 2 級 ‧ 2022-01-25 10:33:14 檢舉

這麼久以前寫文章有幫上忙真的是太棒了XD

我要留言

立即登入留言